<?php

class m_rights_services_RightsService
{
	/**
	 * @var m_rights_services_RightsService
	 */
	private static $instance;
	
	/**
	 * Constructor
	 */
	private function __construct()
	{
		// Code if needed...
	}
	
	/**
	 * @return m_rights_services_RightsService
	 */
	public static function getInstance()
	{
		if (self::$instance === null)
		{
			self::$instance = new self();
		}
		return self::$instance;
	}


/************************* GENERIC *****************************/

	/**
	 * Get a module path by is his name
	 *
	 * @param string $moduleName
	 * @return array
	 */
	public function getModulesPathByName($moduleName)
	{
		$fileResolver = f_file_Resolver::getInstance();
		return $fileResolver->getModulePath($moduleName);
	}

	/**
	 * Get all modules name
	 *
	 * @return array
	 */
	public function getAllModulesName()
	{
		$fileResolver = f_file_Resolver::getInstance();
		$modulesDir = $fileResolver->fromDirectory(MODULES_DIR)->setMask("*")->setRecursive(false)->find();
		$webappDir = $fileResolver->fromDirectory(WEBAPP_MODULES_DIR)->setMask("*")->setRecursive(false)->find();

		$tmp = array();
		if(is_array($modulesDir) && count($modulesDir) > 0)
		{
			foreach($modulesDir as $mdir)
			{
				$mtmp = explode(DS,$mdir);
				$tmp[] = end($mtmp);
			}
		}

		if(is_array($webappDir) && count($webappDir) > 0)
		{
			foreach($webappDir as $wdir)
			{
				$wtmp = explode(DS,$wdir);
				$tmp[] = end($wtmp);
			}
		}

		return array_unique($tmp);
	}

	/**
	 * Get all actions files path from a module path
	 *
	 * @param string $path
	 * @return array
	 */
	public function getActionsFilesPathFromModulePath($path)
	{
		$fileResolver = f_file_Resolver::getInstance();
		return $fileResolver->fromDirectory($path.DS."actions")->setMask("*Action.class.php")->setRecursive(true)->find();
	}

	/**
	 * Get all actions names from a module path
	 *
	 * @param string $path
	 * @return array
	 */
	public function getActionsNamesFromModulePath($path)
	{
		$actionsFiles = $this->getActionsFilesPathFromModulePath($path);

		$res = array();
		foreach($actionsFiles as $actionFile)
		{
			$tmp = explode(DS,$actionFile);
			$name = explode("Action",$tmp[count($tmp)-1]);

			if($tmp[count($tmp)-2] == "block")
			{
				$res[] = $tmp[count($tmp)-2]."_".$name[0];
			}
			else
			{

				$res[] = $name[0];
			}
		}

		return $res;
	}


/************************** ROLES ******************************/
	public function getAllRights()
	{
		$modules = $this->getAllModulesName();
		
		$allRights = array();
		$rightInfo = array();
		
		foreach($modules as $module)
		{
			$rights = $this->readRightsFromModule($module);

			if(count($rights) > 0)
			{				
				foreach($rights as $right)
				{
					$allRights[] = $module."_".$right['name'];
				}
			}
		}
		return $allRights;
	}


	/**
	 * Save $group's $rules in Database
	 * 
	 * @param m_users_documents_Group $group
	 * @param mixed $rules
	 */	
	public function saveRightsForGroup($group, $rules)
	{
		$groupRights = $group->getRights();

        if(!is_array($rules))
        {
            $rules = split("[,; ]", $rules);
        }

		if(empty($groupRights) == true)
		{
			$doc = m_rights_documents_Rights::createNew()->setAuthor("pagizer")->setRules(serialize($rules))->save();
			$group->addRightsById($doc->getUniqueId())->save();
		}
		else
		{
			$groupRights = $groupRights[0];
			$groupRights->setRules(serialize($rules))->save();
		}	
	}	
	
	/**
	 * Read the Rights file of $moduleName and return all rules
	 *
	 * @param string $moduleName
	 * @return array
	 */		
	public function readRightsFromModule($moduleName)
	{
        $file = f_file_Resolver::getModuleFilePath($moduleName, "config".DIRECTORY_SEPARATOR."rights.xml");
		
		$rights = array();
		
		if(!is_null($file))
		{
			$xmlFileObject = f_xml_Xpath::getInstance($file);
			
			$properties = $xmlFileObject->fromXpath("rule")->getElements();
							
			/* @var $var f_xml_XmlElement */
			foreach($properties as $var)
			{
				$rights[] = $var->getAttributes();
			}			
		}
		
		return $rights;		
	}
	
	/**
	 * Read the Rigths file from module name and return the title name
	 * 
	 * @param $moduleName
	 * @return string
	 */
	public function readTitleRightsFromModule($moduleName)
	{
        $file = f_file_Resolver::getModuleFilePath($moduleName, "config".DIRECTORY_SEPARATOR."rights.xml");
		
		if(!is_null($file))
		{
			$xmlFileObject = f_xml_Xpath::getInstance($file);	
			$title = $xmlFileObject->fromXpath("rights")->getAttributeValue("name");
		}

		return $title;	
	}

	/**
	 * Return an array of $moduleName's rules that $group can access
	 *
	 * @param string $moduleName
	 * @param m_users_documents_Group $group
	 * @return array
	 */		
	public function readRightsFromModuleForGroup($moduleName,$group)
	{
		$rights = $this->readRightsFromModule($moduleName);

		$groupRights = array();
		$groupRights = $this->getRightsFromGroup($group);

		$resRights = array();
		foreach($rights as $rule)
		{
			$val = false;
			if(in_array($moduleName."_".$rule['name'],$groupRights))
			{
				$val = true;		
			}
			$resRights[$rule['name']] = $val;			
		}
		
		return $resRights;
	}

	/**
	 * Return an array of group's rules
	 * 
	 * @param $group
	 * @return array
	 */
	public function getRightsFromGroup($group)
	{
		$tabRights = $group->getRights();
		$groupRights = array();
		if(empty($tabRights) == false)
		{
			$tabRights = $tabRights[0];
			$groupRights = unserialize($tabRights->getRules());
		}

		return $groupRights;
	}
	
	/**
	 * Check if the Rights file of the given $moduleName exist
	 *
	 * @return boolean
	 */	
	public function checkRightsFileExistForModule($moduleName)
	{
        $file = f_file_Resolver::getModuleFilePath($moduleName, "config".DS."rights.xml");
		
		return !is_null($file);
	}

/************************** RIGHTS ******************************/    
    public function getRightsFromDocument($groupId, $module, $document)
    {
        $groups = f_relation_Manager::getInstance()->getParentsIdsByDocType($groupId, "modules_users/group");
        $groups[]['parent_id'] = $groupId;

        $validResult = array();

        $nbGroup = count($groups);
        foreach($groups as $i => $group)
        {
            $result = $this->getRightFromAcl($group['parent_id'], $module, $document);

            if(empty($result) && empty($validResult))
            {
                if($i < $nbGroup-1)
                {
                    $validResult = $this->getRightsFromDocument($group['parent_id'], $module, $document);

                    foreach($validResult as $i => $value)
                    {
                        $validResult[$i] = $value == '1' && $i != 'front' ? '2' : $value;
                    }

                    continue;
                }
                elseif($nbGroup == 1)
                {
                    $validResult = array('1', '1', '1', '1', '1');
                }
                
                $keys = array('front', 'read', 'edit', 'add', 'delete');
                $result = array_combine($keys, $validResult);

                $users = $this->getUserByRightsFromDocument($document, $module, 'front', '3');
                if(!empty($users))
                {
                    $result['front'] = '0';
                }

                return $result;
            }
            elseif(!empty($result))
            {
                foreach($result as $key => $value)
                {
                    if(isset($validResult[$key]) && ($validResult[$key] == '2' || $validResult[$key] == '4'))
                    {
                        continue;
                    }

                    if($value > '0')
                    {
                        if($key == 'front')
                        {
                            $users = $this->getUserByRightsFromDocument($document, $module, 'front', '3');
                            if(!empty($users))
                            {
                                if($group['parent_id'] != $groupId && in_array($group['parent_id'], $users))
                                {
                                    $validResult[$key] = '4';
                                }
                                else
                                {
                                    $validResult[$key] = in_array($groupId, $users) ? '3' : '0';
                                }
                            }
                            else
                            {
                                $validResult[$key] = '1';
                            }
                        }
                        else
                        {
                            $validResult[$key] = $group['parent_id'] == $groupId ? '1' : '2';
                        }
                    }
                    else
                    {
                        $validResult[$key] = $group['parent_id'] == $groupId && count($groups) > 1 ? $validResult[$key] : '0';
                        
                        if($key == 'front' && $groupId == $group['parent_id'] && $result[$key] != '1')
                        {
                            $users = $this->getUserByRightsFromDocument($document, $module, 'front', '3');
                            if(empty($users))
                            {
                                $this->saveRightsForDocument($groupId, $module, $document, 'front', array('front' => '1'));
                                $validResult[$key] = '1';
                            }
                        }
                    }
                }
            }
        }

        return $validResult;
    }

    public function getUserByRightsFromDocument($document, $module, $rule, $value = '1')
    {
        $dbProvider = f_database_Provider::getInstance();
        $sql = 'SELECT `user_id` FROM `f_acl` WHERE `document_uid` = :documentId AND `document_lang` = :documentLang AND `module` = :module AND `' . $rule . '` = '.$value;
        $dbProvider->setQuery($sql)->execute(array('documentId' => $document->getUniqueId(), 'documentLang' => $document->getLang(), 'module' => $module));
        return $dbProvider->getQueryResults(PDO::FETCH_COLUMN);
    }

    public $jsTab;
    public function saveRightsForDocument($groupId, $module, $document, $rule, $rights = null)
    {
        $docRights = $this->getRightFromAcl($groupId, $module, $document);

        if(empty($docRights))
        {
            $sql = 'INSERT INTO `f_acl` (`document_uid`,`document_lang`,`module`,`user_id` , `front`, `read`,`edit`,`add`,`delete`) VALUES (:documentId,  :documentLang,  :module,  :group,  :front, :read,  :edit,  :add,  :delete)';
            $docRights = $this->getRightsFromDocument($groupId, $module, $document);
        }
        else
        {
            $sql = 'UPDATE `f_acl` SET `front` = :front, `read` = :read,`edit` = :edit,`add` = :add,`delete` = :delete WHERE `document_uid` = :documentId AND `document_lang` = :documentLang AND `module` = :module AND `user_id` = :group';
        }
        
        $rights = $this->changeRights($docRights, $rule, $rights);
        
        $infos = array('documentId' => $document->getUniqueId(), 'documentLang' => $document->getLang(), 'module' => $module, 'group' => $groupId);
        $args = array_merge($infos, $rights);

        $dbProvider = f_database_Provider::getInstance();
        $dbProvider->setQuery($sql)->execute($args);

        $documents = m_backoffice_services_ListService::getInstance()->getModuleListedDocuments($module);
        $results = f_relation_Manager::getInstance()->getChildsByDocType($document->getUniqueId(), $documents, $document->getLang());
        foreach($results as $result)
        {
            if($rule != 'front')
            {
                unset($rights['front']);
            }
            
            $this->saveRightsForDocument($groupId, $module, $result, $rule, $rights);
        }

        //to keep the hierarchy of with the blocks rights.
        if($document->getDocumentModel() == "modules_website/page")
        {
            $blocks = $document->getAllBlocks();
            $infos['module'] = 'page';

            foreach($blocks as $block)
            {
                $outRights = $document->setBlockRight($groupId, $block['blockId'], $rule, $rights);

                $infos['blockId'] = $block['blockId'];
                $infos['rights'] = $outRights;
                $this->jsTab[] = $infos;
                unset($infos['blockId']);
            }

            $infos['module'] = $module;
        }

        $infos['rights'] = $this->getRightsFromDocument($groupId, $module, $document);
        $this->jsTab[] = $infos;
    }

    /**
     * Get rights from the table ACL
     * @param <type> $groupId
     * @param <type> $module
     * @param <type> $document
     * @return <type> array(5)
     */
    private function getRightFromAcl($groupId, $module, $document)
    {
        $dbProvider = f_database_Provider::getInstance();
        $sql = 'SELECT `front`, `read`, `edit`, `add`, `delete` FROM `f_acl` WHERE `document_uid` = :documentId AND `document_lang` = :documentLang AND `module` = :module AND `user_id` = :group LIMIT 1';
        $dbProvider
            ->setQuery($sql)
            ->execute(array('documentId' => $document->getUniqueId(), 'documentLang' => $document->getLang(), 'module' => $module, 'group' => $groupId));

        $result = $dbProvider->getQueryResults(PDO::FETCH_ASSOC);
        return !empty($result) ? $result[0] : array();
    }

    /**
     *  Change the rights. true -> false : false -> true.
     * @param <type> $docRights -> actual rights
     * @param <type> $rule -> rule to change
     * @return <type> array() with key 'front', 'read', 'edit', 'add', 'delete'
     */
    public function changeRights($docRights, $rule, $rights = null)
    {
        if(is_null($rights) && !is_null($rule))
        {
            $keys = array_keys($docRights);

            if($rule == 'front')
            {
                $rights = array_values($docRights);
                $rights[0] = ($docRights['front'] < '2') ? '3' : '0';
            }
            else
            {
                switch($rule)
                {
                    case "read":
                        $rights = ($docRights[$rule] == '0') ? array('1', '0', '0', '0') : array('0', '0', '0', '0');
                    break;
                    case "edit":
                        $rights = ($docRights[$rule] == '0') ? array('1', '1', '0', '0') : array('1', '0', '0', '0');
                    break;
                    case "add":
                        $rights = ($docRights[$rule] == '0') ? array('1', '1', '1', '0') : array('1', '1', '0', '0');
                    break;
                    case "delete":
                        $rights = ($docRights[$rule] == '0') ? array('1', '1', '1', '1') : array('1', '1', '1', '0');
                    break;
                }

                array_unshift($rights, $docRights['front']);
            }

            $rights = array_combine($keys, $rights);
        }
        else
        {
            if(count($rights) != 5)
            {
                foreach($docRights as $key => $right)
                {
                    $rights[$key] = isset($rights[$key]) ? $rights[$key] : $docRights[$key];
                }
            }
        }

        return $rights;
    }


/********************* GET PERMISSIONS **************************/
	/**
	 * Get all user's rights
	 *
	 * @param m_users_documents_Users $user
	 * @return array
	 */	
	public function getUserRights($user)
	{
		if(is_null($user))
		{
			throw new Exception("Parameter given is null!");
		}
		
		if(empty($user) == true)
		{
			throw new Exception("Parameter given is empty!");
		}
				
		$res = array();
		$rights = array();
		$res = $this->getInheritRightsForGroup($user);
		
		$rights = $this->getRightsFromGroup($user);
		
		if(!empty($rights))
		{
			foreach($rights as $right)
			{
				if(!in_array($right,$res))
				{
					$res[] = $right;
				}
			}
		}

		return $res;
	}
	
	/**
	 * Get all rights which are inherit from an sub group
	 * 
	 * @param m_users_documents_Group $group
	 * @return array
	 */
	public function getInheritRightsForGroup($group)
	{
		if(is_null($group))
		{
			throw new Exception("Parameter given is null!");
		}
		
		if(empty($group))
		{
			throw new Exception("Parameter given is empty!");
		}
		
		$inheritRights = array();
		$parents = f_relation_Manager::getInstance()->getParentsByDocType($group->getUniqueId(), "modules_users/group");

		foreach($parents as $parent)
		{
			$rights = $this->getRightsFromGroup($parent);
			foreach($rights as $right)
			{
				if(!in_array($right,$inheritRights))
				{
					$inheritRights[] = $right;
				}
			}
		}
		
		return $inheritRights;
	}
	
	/**
	 * Show if a group is inherit from the su group
	 * @param $group
	 * @return bool
	 */
	public function isInheritSuGroup($group)
	{
		$superGroup = f_document_Provider::getInstance()->loadDocuments("modules_users/group")->retrieveFirstDocument();
		$parents = f_relation_Manager::getInstance()->getParentsByDocType($group->getUniqueId(), "modules_users/group");

		if($superGroup->getUniqueId() == $group->getUniqueId())
		{
			return true;
		}
		
		foreach($parents as $parent)
		{
			if($parent->getUniqueId() == $superGroup->getUniqueId())
			{
				return true;
			}
		}
		
		return false;
	}
}